package org.aspire.core.loop;

import org.aspire.core.sys.GSystem;

/******************************************************************************
 * An object that regulates FPS rate of the game.
 * 
 * @author Chris Molini	(jRabbit)
 * @author Golden T Studios (GTGE)
 * @author JPACarabuena
 *****************************************************************************/
public abstract class LoopController {
	
	// ========================================================================
	// Constants
	// ========================================================================
	
	// ========================================================================
	// Static Methods
	// ========================================================================
	
	// ========================================================================
	// Fields
	// ========================================================================
	
	/**************************************************************************
	 * Whether or not this controller is running.
	 * 
	 * Default: false
	 *************************************************************************/
	protected boolean running;
	
	/**************************************************************************
	 * Whether or not to sleep the thread. By disabling this, the associated
	 * process will run as fast as it can (useful for simulating over a large
	 * number of iterations).
	 * 
	 * Default: true.
	 *************************************************************************/
	protected boolean regulate;
	
	/**************************************************************************
	 * Whether or not the update variable should be a constant, "predicted"
	 * value - i.e., resolution / fps.
	 * 
	 * Default: true.
	 *************************************************************************/
	protected boolean smooth;
	
	/**************************************************************************
	 * The units for the timer to use - the number of ticks in a second.
	 * 
	 * Default: 1000 - i.e., the controller reports in milliseconds.
	 *************************************************************************/
	protected int resolution;
	
	/**************************************************************************
	 * How many iterations we want per second.
	 * 
	 * Default: 60
	 *************************************************************************/
	protected int fps;
	
	/**************************************************************************
	 * Keeps track of when the time was last recorded so we can know the
	 * interval between checks.
	 *************************************************************************/
	protected long lastCall;
	
	/**************************************************************************
	 * The duration of the recorded interval, in the set resolution.
	 *************************************************************************/
	protected long duration;
	
	/**************************************************************************
	 * The FPS counter for this loop controller.
	 *************************************************************************/
	protected FPSCounter fpsCounter;
	
	// ========================================================================
	// Constructors
	// ========================================================================
	
	/**************************************************************************
	 * Construct a new loop controller.
	 *************************************************************************/
	public LoopController() {
		this.running = false;
		this.regulate = true;
		this.smooth = true;
		this.resolution = 1000;
		this.fps = 60;
		this.fpsCounter = new FPSCounter();
	}
	
	// ========================================================================
	// Getters & Setters
	// ========================================================================

	/**************************************************************************
     * Returns whether the controller is currently running or not.
     * <p> 
	 * Timer is running when {@link #start()} is called.
	 * 
	 * @return	Whether or not the controller is running.
	 *************************************************************************/
	public boolean isRunning() {
		return this.running;
	}
	
	/**************************************************************************
	 * Returns the desired interval of time.
	 * <p>
	 * If delta smoothing is on, it actually ignores the value calculated in
	 * advance() and returns a predicted, constant value instead.
	 * 
	 * @return The interval of time to update, in the set resolution.
	 *************************************************************************/
	public long getDelta() {
		return this.smooth ? (this.resolution / this.fps) : this.duration;
	}
	
	/**************************************************************************
	 * Returns the units that timer use.
	 * 
	 * @return	the loop controller resolution.
	 *************************************************************************/
	public int getResolution() {
		return this.resolution;
	}
	
	/**************************************************************************
	 * Checks if this loop controller is regulating process.
	 * 
	 * @return	whether or not this loop controller is regulating process.
	 *************************************************************************/
	public boolean isRegulating() {
		return this.regulate;
	}
	
	/**************************************************************************
	 * Checks if the update variable is constant or predicted.
	 * 
	 * @return	whether or not the update is smooth.
	 *************************************************************************/
	public boolean isSmooth() {
		return this.smooth;
	}
	
	/**************************************************************************
	 * Returns the FPS for this loop controller.
	 * 
	 * @return	how many iterations per second.
	 *************************************************************************/
	public int getFPS() {
		return this.fps;
	}
	
	/**************************************************************************
	 * Returns the actual FPS achieved.
	 * 
	 * @return	the actual FPS achieved.
	 *************************************************************************/
	public int getCurrentFPS() {
		return this.fpsCounter.getCurrentFPS();
	}

	/**************************************************************************
	 * Sets the unit for the timer to use to the specified resolution.
	 * 
	 * @param resolution	the new timer resolution.
	 *************************************************************************/
	public void setResolution(int resolution) {
		this.resolution = resolution;
	}
	
	/**************************************************************************
	 * Enable or disable process regulation.
	 * 
	 * @param regulate	whether or not regulating process is enabled.
	 *************************************************************************/
	public void enableRegulate(boolean regulate) {
		this.regulate = regulate;
	}
	
	/**************************************************************************
	 * Enable or disable smooth update.
	 * 
	 * @param smooth	whether or not smooth update is enabled.
	 *************************************************************************/
	public void enableSmoothUpdate(boolean smooth) {
		this.smooth = smooth;
	}
	
	/**************************************************************************
	 * Sets the FPS for this controller to the specified FPS.
	 * 
	 * @param fps	the new iterations per second.
	 *************************************************************************/
	public void setFPS(int fps) {
		this.fps = fps;
	}

	// ========================================================================
	// Override Methods
	// ========================================================================

	// ========================================================================
	// Methods
	// ========================================================================
	
	/**************************************************************************
	 * Starts the timer.
	 *************************************************************************/
	public void start() {
		if(this.running) {
			this.stop();
		}
		
		this.running = true;
		
		this.fpsCounter.refresh();
	}
	
	/**************************************************************************
	 * Stops the timer.
	 *************************************************************************/
	public void stop() {
		this.running = false;
	}

	/************************************************************************** 
	 * Sleeps for awhile to achieve requested frame per second.
	 *************************************************************************/
	public abstract void sleep();
	
	/**************************************************************************
	 * Advances the clock to record the amount of time that has passed.
	 *************************************************************************/
	public abstract void tick();
	
	// ========================================================================
	// Inner & Anonymous Classes
	// ========================================================================
	
	/**************************************************************************
	 * A utility class to calculate timer frame per seconds (FPS) in a 
	 * convenient way.
	 * 
	 * @author Golden T Studios (GTGE)
	 * @author JPACarabuena
	 *************************************************************************/
	protected class FPSCounter {
		
		// ====================================================================
		// Fields
		// ====================================================================
		
		/**********************************************************************
		 * The last time the FPS is counted.
		 *********************************************************************/
		private long lastCount;
		
		/**********************************************************************
		 * The real FPS achieved.
		 *********************************************************************/
		private int currentFPS;
		
		/**********************************************************************
		 * Temporary variable for counting FPS.
		 *********************************************************************/
		private int frameCount;
		
		// ====================================================================
		// Constructor
		// ====================================================================
		
		/**********************************************************************
		 * Construct a new FPS counter.
		 *********************************************************************/
		public FPSCounter() {
			
		}
		
		// ====================================================================
		// Methods
		// ====================================================================
		
		/**********************************************************************
		 * Refresh the FPS counter, reset the FPS to 0 and the timer counter to
		 * start counting from current time.
		 **********************************************************************
		 */
		public void refresh() {
			this.frameCount = 0;
			this.lastCount = GSystem.getTime();
		}
		
		/**********************************************************************
		 * Calculate the frame per second.
		 *********************************************************************/
		public void calculateFPS() {
			this.frameCount++;
			if(GSystem.getTime() - this.lastCount > 
					LoopController.this.resolution) {
				this.lastCount = GSystem.getTime();
				this.currentFPS = this.frameCount;
				this.frameCount = 0;
			}
		}
		
		/**********************************************************************
		 * Gets the current FPS.
		 * 
		 * @return	the current frame per second.
		 *********************************************************************/
		public int getCurrentFPS() {
			return this.currentFPS;
		}
	}
}